/*********************************************************************************
 * Software License Agreement (BSD License)
 * Copyright (c) 2012, Carlos D. Correa (info@ngraph.org)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of netzen nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *********************************************************************************/
#include "viewManager.h"
#include "controller.h"
#include "view.h"
#include "mouseUtils.h"
#include "renderUtils.h"
#include <assert.h>

ViewManager::ViewManager(Controller *controller) {
	this->controller = controller;
}

void ViewManager::drawViews() {
	for(int k=(int) orderRendering.size()-1;k>=0;k--) {
		int i = orderRendering[k];
		if(views[i]->isVisible()) views[i]->draw();
	}
}

void ViewManager::moveToTop(int top) {
	for(unsigned int k=0;k<orderRendering.size();k++) {
		int i = orderRendering[k];
		if(i==top) {
			for(int j=k;j>0;j--) {
				orderRendering[j] = orderRendering[j-1];	
			}
			orderRendering[0] = top;
			return;
		}
	}
}

void ViewManager::viewAdded(View *view) {
	orderRendering.push_back(views.size());
	views.push_back(view);
	moveToTop(views.size()-1);
	for(unsigned int i=0;i<views.size();i++) {
		if(views[i]->isSelected()) {
			views[i]->setSelected(false);
		}
	}
	controller->updateRender();
}



void ViewManager::render(void)
{
	glClearColor(1,1,1,1);
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(1,0,0);
	
	glGetDoublev(GL_PROJECTION_MATRIX, P);
	glGetDoublev(GL_MODELVIEW_MATRIX, M);
	
	if(views.size()==0) {
		glColor4f(0,0,0,1);
		drawText(-0.5, 0.0, "Create a view (Menu: View/Create) to start visualizing your data", "Arial", 12);
	}
	drawViews();
	
	glFinish();
	
}

/*********************************************************************************
 INTERACTION
 *********************************************************************************/


void ViewManager::handleKey(unsigned char key, int , int )
{
    switch (key)
    {
		case 'q':
			exit (1);
			break;
    }
}

void ViewManager::tic(void) {  
	for(unsigned int i=0;i<views.size();i++) {
		if(views[i]->isSelected()) {
			views[i]->tic();
			return;
		}
	}
}



/*********************************
 MOUSE INTERACTION
 *******************************/

void ViewManager::toWorld(int ix, int iy, float &x, float &y) {
	glGetIntegerv(GL_VIEWPORT, V);
	double xw,yw,zw;
	gluUnProject(ix,V[3]-iy,0,
				 M, P, V,
				 &xw, &yw, &zw);  
	x = (float) xw;
	y = (float) yw;
}


bool ViewManager::click(float x, float y, int modifiers) {
	for(unsigned int k=0; k<orderRendering.size();k++) {
		int i = orderRendering[k];
		assert(i>=0 && i<(int) views.size());
		views[i]->setSelected(false);
		if(views[i]->isVisible() && views[i]->encloses(x,y)) {
			views[i]->setSelected(true);      
			views[i]->click(x,y,modifiers);
			moveToTop(i);
			return true;
		}
	}
	return false;
}

void ViewManager::release(float x, float y, int modifiers) {
	curstate = NZ_UP;
	for(unsigned int i=0;i<views.size();i++) {
		if(views[i]->isSelected()) {
			views[i]->release(x,y,modifiers);
			return;
		}
	}
}

void ViewManager::drag(float x, float y, float dx, float dy, int modifiers) {
	for(unsigned int i=0;i<views.size();i++) {
		if(views[i]->isVisible() && views[i]->isSelected()) {
			views[i]->drag(x, y, dx,dy, modifiers);
			return;
		}
	}
}


void ViewManager::mouseMove(float x, float y, float dx, float dy, int modifiers) {
	for(unsigned int i=0;i<views.size();i++) {
		if(views[i]->isVisible() && views[i]->isSelected()) { // && views[i]->encloses(x,y)) {
			views[i]->mouseMove(x, y, dx,dy, modifiers);
		}
	}
}


void ViewManager::mouseMoveEvent(int ix, int iy, int button, int modifiers) { 
	float x;
	float y;
	toWorld(ix,iy,x,y);
	if(button==0) {
		curbutton = button;
		release(x,y, modifiers);
	}
	//	printf("Main: Motion Func Btn %d State %d\n", curbutton, curstate);
	if(curstate==NZ_DOWN && curbutton==NZ_LEFT_BUTTON) {
		drag(x, y, -lastwx + x, - lastwy + y, modifiers); 
	}
	if(!curstate==NZ_DOWN) {
		mouseMove(x, y, -lastwx + x, - lastwy + y, modifiers); 
	}
	lasty = iy;
	lastx = ix;
	lastwx = x;
	lastwy = y;
}


void ViewManager::mouseEvent(int ix, int iy, int button, int state, int modifiers) {
	curstate = state;
	curbutton = button;
	
	
	//	printf("Main: Mouse Func Btn %d State %d\n", button, state);
	float x;
	float y;
	
	toWorld(ix,iy,x,y);
	lastx = ix;
	lasty = iy;
	lastwx = x;
	lastwy = y;
	
	shiftPressed = (modifiers & NZ_ACTIVE_SHIFT);
	ctrlPressed = (modifiers & NZ_ACTIVE_CTRL);
	
	if((button == NZ_LEFT_BUTTON && state==NZ_DOWN)){
		click(x, y, modifiers);
	}
	
	if(button==NZ_LEFT_BUTTON && state==NZ_UP) {
		release(x,y, modifiers);
	}
}

void ViewManager::resize(int w, int h) {
	glViewport(0,0,w,h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	float a = (float) w/(float) h;
	gluOrtho2D(-a, a, -1.0, 1.0);
	
}


